#include <ParallelJacobi.hpp>
#include <iostream>
#include <fstream>
#include <vector>
#include <mpi.h>
#include <omp.h>

const int num_threads_in_process = 2;

int main (int argc, char* argv[]) {

	// Inicia o ambiente paralelo
	MPI::Init(argc, argv);

	// Configura numero maximo de threads omp
	omp_set_num_threads(num_threads_in_process);

	// Obtem nro do processo e quantidade de processos
	int pid = MPI::COMM_WORLD.Get_rank();
	int processes = MPI::COMM_WORLD.Get_size();
	
	// Abre arquivo para leitura
	std::fstream file(argv[1], std::fstream::in);

	int n;
	int rowTest;
	double error;
	int iterations;

	// Prepara impressao (6 casas decimais)
	std::cout.setf(std::ios::fixed, std::ios::floatfield);
    std::cout.precision(6);

    //  Le arquivo de entrada
	file >> n >> rowTest >> error >> iterations;

	std::vector<std::vector<double> > A(n, std::vector<double>(n));
	std::vector<double> B(n);
	std::vector<double> x(n,0.0);

	for (int i = 0; i < n; i++)
		for (int j = 0; j < n; j++)
			file >> A[i][j];
	for (int i = 0; i < n; i++)
		file >> B[i];

	// Fecha arquivo de entrada
	file.close();

	// Sincroniza processos
	MPI::COMM_WORLD.Barrier();

	if (!pid) std::cout << "File has been read successfuly\n";

	// Prepara para calcular tempo gasto
	double wastedTime = -MPI::Wtime();

	// Cria solucionador paralelo
	ParallelJacobi jacobi(A,B,x,error,iterations, pid, processes);
	// Soluciona o problema
	x = jacobi.solve(&iterations);

	// Calcula tempo gasto
	wastedTime += MPI::Wtime();

	// Se for processo mestre, imprime solucao
	if (!pid) {
		std::cout << "Time: " << wastedTime << std::endl;
		double rowTestResult = 0.0;
		for (int i = 0; i < n; i++)
			rowTestResult += x[i] * A[rowTest][i];
		std::cout << "Iterations: " << iterations << std::endl;
		std::cout << "RowTest: " << rowTest << "=> [" <<
				rowTestResult << "] =? " << B[rowTest] << std::endl;
	}

	// Finaliza ambiente paralelo
	MPI::Finalize();
	return 0;
}
